/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2007-2008 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"
#if defined(UMC_ENABLE_AVS_VIDEO_ENCODER)

#include <trace_log.h>
#include "umc_avs_enc_compressor.h"
#include "umc_avs_dec_tables.h"

namespace UMC
{

void AVSCompressor::DisassembleIMacroBlock(void)
{
    Ipp32s edge_type = 0;
    Ipp32u lumaCBP, chromaCBP;
    Ipp32u maxBitSize = 0, mbBitSize = 0, extraBitSize = m_extraBitSize;
    Ipp32u QP, prevQP, chromaQP;
    Ipp16s *psCoeffs;
    Ipp32u foundBestQP;

    // set parameters for I macroblock
    m_pMbInfo->MbType = I_8x8;

    m_pMbInfo->NumCoeffs[0] = 63;
    m_pMbInfo->NumCoeffs[1] = 63;
    m_pMbInfo->NumCoeffs[2] = 63;
    m_pMbInfo->NumCoeffs[3] = 63;
    m_pMbInfo->NumCoeffs[4] = 63;
    m_pMbInfo->NumCoeffs[5] = 63;
    m_pMbInfo->NumCoeffs[6] = 63;
    m_pMbInfo->NumCoeffs[7] = 63;

    // prepare macroblock's absent block edges variable
    edge_type |= (m_pMbInfoLeft) ?
                 (0) : (IPPVC_LEFT_EDGE);
    edge_type |= (m_pMbInfoTop) ?
                 (0) : (IPPVC_TOP_EDGE);
    edge_type |= ((m_disCtx.MbY + 1) * m_disCtx.MbWidth - 1 != MbIndex) ?
                 (0) : (IPPVC_TOP_RIGHT_EDGE);

    // set the initial QP
    QP = m_disCtx.PreviousQP;
    prevQP = QP;
    foundBestQP = 0;

    // set working bit sizes
    extraBitSize = m_extraBitSize;
    maxBitSize = m_maxMbBitSize;

    // main working cycle
    do
    {
        // set the destination pointer
        psCoeffs = m_pWriteCoeffs;

        // disassemble luminance blocks
        ippiDisassembleLumaIntra_AVS_16s8u_C1R(m_disCtx.m_pSrcPlanes8u[0],
                                               m_disCtx.m_iPitch,
                                               m_recCtx.m_pPlanes8u[0],
                                               m_recCtx.m_iPitch,
                                               &psCoeffs,
                                               m_pMbInfo->IntraLumaPredMode,
                                               &lumaCBP,
                                               QP,
                                               edge_type);

        // disassemble chrominance blocks
        chromaQP = AVSChromaQPTable[QP];
        ippiDisassembleChroma420Intra_AVS_16s8u_C1R(m_disCtx.m_pSrcPlanes8u + 1,
                                                 m_disCtx.m_iPitch,
                                                 m_recCtx.m_pPlanes8u + 1,
                                                 m_recCtx.m_iPitch,
                                                 &psCoeffs,
                                                 m_pMbInfo->IntraChromaPredMode,
                                                 &chromaCBP,
                                                 chromaQP,
                                                 edge_type);

        // set new macroblock parameter
        // it is required to calculate required MB size correctly
        m_pMbInfo->QP = QP;
        m_pMbInfo->MbCBP = (chromaCBP << 4) | lumaCBP;

        // check break cycle condition
        if (m_disCtx.FixedQP)
            break;

        // calculate required bit stream for this macroblock
        mbBitSize = GetSizeIMacroBlock();

        //
        // check quit conditions
        //

        // we reached a QP bound, go exit
        if (((mbBitSize < maxBitSize) && (0 == QP)) ||
            ((mbBitSize > maxBitSize) && (63 == QP)))
        {
            break;
        }
        // this macroblock is pretty cool fitting the required size
        if ((mbBitSize >= maxBitSize) &&
            (mbBitSize <= maxBitSize + extraBitSize))
        {
            break;
        }
        // the macroblock doesn't fit the size, continue coding
        if (mbBitSize < maxBitSize)
        {
            // it is a QP, that we are looking for
            if (prevQP < QP)
            {
                break;
            }

            // set new QP
            prevQP = QP;
            QP -= 1;
        }
        else
        {
            // set new QP
            prevQP = QP;
            QP += 1;
        }

        // it is unreal condition, just to make the loop infinite
    } while (QP != prevQP);
/*
    {
        char cStr[256];
        sprintf(cStr, "% 4d ", GetSizeIMacroBlock());
        OutputDebugString(cStr);

        if (m_recCtx.MbX + 1 == m_recCtx.MbWidth)
        {
            sprintf(cStr, "[% 4d]\n", m_recCtx.MbY);
            OutputDebugString(cStr);
        }
    }*/

    // save previous QP
    m_disCtx.PreviousQP = QP;

    // set the coefficient pointer
    m_pWriteCoeffs = psCoeffs;

    // update available extra bits
    if (0 == m_disCtx.FixedQP)
    {
        m_extraBitSize = IPP_MAX(0, (Ipp32s) (maxBitSize + extraBitSize - mbBitSize));
    }

    FillUpMeMbStatIMacroBlock();

    WRITE_TRACE_LOG(_rec_tst_enc, "CurrentQP", m_pMbInfo->QP);

    WRITE_TRACE_LOG_BYTE_BLOCK(_rec_tst_enc, "Y block", m_recCtx.m_pPlanes8u[0], m_recCtx.m_iPitch, 16, 16);

    WRITE_TRACE_LOG(_rec_tst_enc, "chromaQP", chromaQP);

    WRITE_TRACE_LOG_BYTE_BLOCK(_rec_tst_enc, "U block", m_recCtx.m_pPlanes8u[1], m_recCtx.m_iPitch, 8, 8);
    WRITE_TRACE_LOG_BYTE_BLOCK(_rec_tst_enc, "V block", m_recCtx.m_pPlanes8u[2], m_recCtx.m_iPitch, 8, 8);

} // void AVSCompressor::DisassembleIMacroBlock(void)

} // namespace UMC

#endif // defined(UMC_ENABLE_AVS_VIDEO_ENCODER)
